package com.markhsiu.main;

import java.util.AbstractSet;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import com.markhsiu.core.constant.Bits;
import com.markhsiu.store.StoreDB;

public class MarkMap {

	private byte[] bytes;
	private int size = 0;
	private boolean extension = false;// 是否扩展序列化

	private int capacity = 0;
	private int count = 0;
	private int threshold;

	static final int DEFAULT_INITIAL_CAPACITY = 1 << 3; // aka 3

	static final float DEFAULT_LOAD_FACTOR = 0.75f;
	private Entry[] table;

	public MarkMap() {
		init();
	}

	public MarkMap(boolean extension) {
		this.extension = extension;
		if (extension) {
			// bad
			bytes = StoreDB.newBytes();
		}
		init();
	}

	private void init() {
		capacity = DEFAULT_INITIAL_CAPACITY;
		threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
		table = new Entry[capacity];
	}

	public String get(String key) {
		Entry[] tab = table;
		int hash = hash(key);
		int index = (hash & 0x7FFFFFFF) % capacity;
		for (Entry e = tab[index]; e != null; e = e.next) {
			if ((e.hash == hash) && e.key.equals(key)) {
				return e.value;
			}
		}
		return null;
	}

	@SuppressWarnings("deprecation")
	public void put(String key, String value) {

		if (extension) {
			int len = key.length();
			bytes[size++] = (byte)len;
			key.getBytes(0, len, bytes, size);
			size += len;

			len = value.length();
			Bits.putShort(bytes, size, (short) len);
			size += 2;
			value.getBytes(0, len, bytes, size);
			size += len;
			return ;
		}
		
		// int hashCode = hashCode(key);

		// Makes sure the key is not already in the hashtable.
//		Entry[] tab = table;
		int hash = hash(key);
		int index = (hash & 0x7FFFFFFF) % capacity;
//		Entry entry = tab[index];
//		for (; entry != null; entry = entry.next) {
//			if ((entry.hash == hash) && entry.key.equals(key)) {
//				entry.value = value;
//				break;
//			}
//		}

		addEntry(hash, key, value, index);
	}

	private void addEntry(int hash, String key, String value, int index) {

		Entry[] tab = table;
		if (count >= threshold) {
			// Rehash the table if the threshold is exceeded
			rehash();

			tab = table;
			hash = hash(key);
			index = (hash & 0x7FFFFFFF) % capacity;
		}

		tab[index] = new Entry(hash, key, value, tab[index]);
		count++;

	}

	private void rehash() {
		int oldCapacity = capacity;
		Entry[] oldMap = table;

		// overflow-conscious code
		capacity = (oldCapacity << 1) + 1;
		Entry[] newMap = new Entry[capacity];

		threshold = (int) (capacity * DEFAULT_LOAD_FACTOR);
		table = newMap;

		for (int i = oldCapacity; i-- > 0;) {
			for (Entry old = oldMap[i]; old != null;) {
				Entry e = old;
				old = old.next;

				int index = (e.hash & 0x7FFFFFFF) % capacity;
				e.next = newMap[index];
				newMap[index] = e;
			}
		}
	}

	char[] noUsedbuffer = null;
	
	public char[] getCharBuffer(){
		if(noUsedbuffer == null){
			noUsedbuffer = new char[StoreDB.MAX_BYTE];
		}
		return noUsedbuffer;
	}
	
	public void put(String key, byte[] data, int offset, int length) {
		char[] buffer = getCharBuffer();
	 
		for (int i = 0; i < length; i++) {
			buffer[i] = (char) data[offset++];
		}
		String V = new String(buffer, 0, length);
		put(key, V);
	}
	
	public void toMap(byte[] data, int offset, int length) {
		char[] buffer = getCharBuffer();
	
		int max = length + offset;
		int len;
		String K;
		String V;

		while (true) {

			// key
			len = data[offset++];
			for (int i = 0; i < len; i++) {
				buffer[i] = (char) data[offset++];
			}
			K = new String(buffer, 0, len);

			// value
			len = Bits.toShort(data[offset++], data[offset++]);
			for (int i = 0; i < len; i++) {
				buffer[i] = (char) data[offset++];
			}
			V = new String(buffer, 0, len);

			put(K, V);
			if (offset == max) {
				break;
			}
		}
	}

	public int toData(byte[] data, int offset) {
		System.arraycopy(bytes, 0, data, offset, size);	
		return size;
	}

	public void toMap(byte[] data) {
		int max = data.length;
		int offset = 0;
		short len;
		String K;
		String V;
		char[] buffer;
		while (true) {

			// key
			len = data[offset++];
			buffer = new char[len];
			for (int i = 0; i < len; i++) {
				buffer[i] = (char) data[offset++];
			}
			K = new String(buffer);

			// value
			len = Bits.toShort(data[offset++], data[offset++]);
			buffer = new char[len];
			for (int i = 0; i < len; i++) {
				buffer[i] = (char) data[offset++];
			}
			V = new String(buffer);
			put(K, V);

			if (offset == max) {
				break;
			}
		}
	}

	

	public boolean containsKey(String key) {
		Entry tab[] = table;
		int hash = hash(key);
		int index = (hash & 0x7FFFFFFF) % capacity;
		for (Entry e = tab[index]; e != null; e = e.next) {
			if ((e.hash == hash) && e.key.equals(key)) {
				return true;
			}
		}
		return false;
	}

	public int size() {
		if (!extension) {
			throw new RuntimeException("没有设置 extension = true");
		}
		return size;
	}

	public int count() {
		return count;
	}
	
	public void reset(){
		size = 0;
		count = 0;
	}

	public void clear() {
		for (int index = capacity; --index >= 0;)
			table[index] = null;
		
		size = 0;
		count = 0;
	}

	public Set<String> keySet() {
		return new KeySet();
	}

	public Entry[] getTable() {
		return table;
	}

	static final int hash(String key) {
		return key.charAt(0);
	}

	public static class Entry {
		final int hash;
		final String key;
		String value;
		Entry next;

		protected Entry(int hash, String key, String value, Entry next) {
			this.hash = hash;
			this.key = key;
			this.value = value;
			this.next = next;
		}

		public boolean equals(Object o) {
			if (!(o instanceof Map.Entry))
				return false;
			Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;

			return (key == null ? e.getKey() == null : key.equals(e.getKey()))
					&& (value == null ? e.getValue() == null : value.equals(e.getValue()));
		}

		public String toString() {
			return key.toString() + "=" + value.toString();
		}
	}

	private class KeySet extends AbstractSet<String> {
		public Iterator<String> iterator() {
			return new Enumerator();
		}

		public int size() {
			return count;
		}

		@SuppressWarnings("unused")
		public boolean contains(String o) {
			return containsKey(o);
		}
	}

	private class Enumerator implements Iterator<String> {
		Entry[] table = MarkMap.this.table;
		int index = table.length;
		Entry entry = null;
		Entry lastReturned = null;

		Enumerator() {
		}

		// Iterator methods
		public boolean hasNext() {
			Entry e = entry;
			int i = index;
			Entry[] t = table;
			/* Use locals for faster loop iteration */
			while (e == null && i > 0) {
				e = t[--i];
			}
			entry = e;
			index = i;
			return e != null;
		}

		public String next() {
			Entry et = entry;
			int i = index;
			Entry[] t = table;
			/* Use locals for faster loop iteration */
			while (et == null && i > 0) {
				et = t[--i];
			}
			entry = et;
			index = i;
			if (et != null) {
				Entry e = lastReturned = entry;
				entry = e.next;
				return e.key;
			}
			throw new NoSuchElementException("MarkMap Enumerator");

		}

		public void remove() {

			if (lastReturned == null)
				throw new IllegalStateException("MarkMap Enumerator");

			Entry[] tab = MarkMap.this.table;
			int index = (lastReturned.hash & 0x7FFFFFFF) % capacity;

			for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
				if (e == lastReturned) {

					if (prev == null)
						tab[index] = e.next;
					else
						prev.next = e.next;
					count--;
					lastReturned = null;
					return;
				}
			}
			throw new ConcurrentModificationException();

		}
	}

}
